/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Distribution.

    Maxsi Distribution is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Distribution is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Distribution. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiDistributionContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiDistributionContributors.h. For more information please see
	MaxsiDistributionContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiDistribution.dll
	Distributing since 2008

	MaxsiTorrent.cpp
	Torrenting!

****************************************************************************/

#include "MaxsiEngine.h"
#include "MaxsiDistributionLink.h"
#include "Thread.h"
#include "Data.h"
#include "MaxsiTorrent.h"
#include "MaxsiFileServer.h"

//=============================================================================
// class MaxsiTorrentData
//=============================================================================

MaxsiTorrentData::MaxsiTorrentData()
{ 
	if (!InitializeCriticalSectionAndSpinCount(&MayWriteToBlocks, 
        0x80000400) ) 
	{
		ME_UNDEFINED_BEHAVIOR("MaxsiTorrentData::MaxsiTorrentData() couldn't initialize InitializeCriticalSectionAndSpinCount(&MayWriteToBlocks)!");
	}
	TorrentName			=	0;
	memset(SpeedCompressed,0,sizeof(SpeedCompressed));
	memset(SpeedUncompressed,0,sizeof(SpeedUncompressed));
	pFiles				=	0;
	pBlocks				=	0;
	nFiles				=	0;
	nBlocks				=	0;
	DirectoryName		=	0;
	DataTotal			=	0;
	DataRequired		=	0;
	DataDownloaded		=	0;
	ComThisSecond		=	0;
	UncomThisSecond		=	0;
	SecondStart			=	0;
	NextSpeedUpdate		=	0;
	ContentArchive		=	0;
	SpeedRecorded		=	0;
}

MaxsiTorrentData::~MaxsiTorrentData()
{
	DeleteCriticalSection(&MayWriteToBlocks);

	if ( TorrentName ) { delete[] TorrentName; }
	if ( ContentArchive ) { delete[] ContentArchive; }
	if ( DirectoryName ) { delete[] DirectoryName; }
	if ( nFiles && pFiles )
	{
		for (size_t I = 0; I < nFiles; I++)
		{
			if ( !pFiles[I] ) { continue; }
			if ( pFiles[I]->FileName )
			{
				delete[]	pFiles[I]->FileName;
			}
			delete pFiles[I];
		}
		delete[] pFiles;
	}
	if ( nBlocks && pBlocks )
	{
		for (size_t I = 0; I < nBlocks; I++)
		{
			if ( !pBlocks[I] ) { continue; }
			delete pBlocks[I];
		}
		delete[] pBlocks;
	}
}

void MaxsiTorrentData::Enter()
{
	EnterCriticalSection(&MayWriteToBlocks);
}

void MaxsiTorrentData::Leave()
{
	LeaveCriticalSection(&MayWriteToBlocks);
}

size_t MaxsiTorrentData::GetBlockSize(size_t Id)
{
	if ( Id == nBlocks -1 )
	{
		return	DataTotal	%	MaxsiTorrent_BlockSize;
	}
	return	MaxsiTorrent_BlockSize;
}


//=============================================================================
//	class ME_TorrentServerClient
//=============================================================================

UINT	g_ME_TorrentMessageIDFunc	=	0;

// Returns the ID of the windows message that ME_Thread uses
const UINT ME_TorrentMessageIDFunc(void)
{
	if ( g_ME_TorrentMessageIDFunc )
		return g_ME_TorrentMessageIDFunc;

	g_ME_TorrentMessageIDFunc	=	RegisterWindowMessageA("ME_TorrentMessageID");

	return		g_ME_TorrentMessageIDFunc;
}

ME_TorrentServerClient::ME_TorrentServerClient()
{
	PeerData		=	0;
	SecondStart		=	0;
	ComThisSecond	=	0;
	UncomThisSecond	=	0;
	Remote			=	0;
	m_State			=	STATE_NOTCONNECTED;

	m_Protocol		=	0;
	m_Mode			=	0;
	m_Command		=	0;

	memset(IsBlockSecure,0,Torrent_MaxBlocksPerPeer*sizeof(bool));
}

ME_TorrentServerClient::~ME_TorrentServerClient()
{
	SetProtocol(0);
	if ( PeerData ) { delete[] PeerData; PeerData = 0; }
	if ( Remote ) { delete[] Remote; Remote = 0; }
}

bool	ME_TorrentServerClient::SetProtocol(int Protocol)
{
	m_Protocol	=	Protocol;
	return true;
}
int		ME_TorrentServerClient::GetProtocol()
{
	return	m_Protocol;
}

bool	ME_TorrentServerClient::Init()				// Attach to a new client
{
	//BaseClass::Init();

	if ( GetProtocol() == 0 ) // if protocol is set, we are already active. Do not change anything
	{
		SetProtocol(MaxsiTorrent_MaxProtocol);
		SetMode(Torrent_Handshake);
	}

	// Todo: Call the function that begins the handshake here, if we are the connecting peer!

	return true;
}

bool	ME_TorrentServerClient::Close()				// Detach from a client
{
	BaseClass::Close();
	return false;
}

bool	ME_TorrentServerClient::InitializeBuffers()
{

	if ( Output ) { DeleteBuffer(Output); }	Output	=	CreateBuffer(8192);
	if ( Input ) { DeleteBuffer(Input); }	Input	=	CreateBuffer(8192);

	return true;
}



size_t	ME_TorrentServerClient::GetBlockSize()
{
	return	MaxsiTorrent_BlockSize;
}


bool	ME_TorrentServerClient::RecievedNBytes(size_t Compressed, size_t Uncompressed, size_t TorrentId)
{
	DWORD	Now		=	GetTickCount();

	if ( SecondStart == 0 )
	{
		SecondStart = Now;
	}

	if ( Now-SecondStart > 1000 )
	{
	
		ComDownSpeed	=	ComThisSecond;
		UncomDownSpeed	=	UncomThisSecond;
		ComThisSecond	=	0;
		UncomThisSecond	=	0;
		SecondStart		=	Now;		
	}

	ComThisSecond	+=	Compressed;
	UncomThisSecond	+=	Uncompressed;

	ME_TorrentServer*	dTorrent		=	dynamic_cast<ME_TorrentServer*>(Server);

	if (dTorrent)
	{
		dTorrent->AddSpeed(Compressed,Uncompressed,TorrentId);
	}

	return true;
}

bool	ME_TorrentServerClient::Connect(char*	Host)
{
	Server->Log("Connecting");

	SetMode(TorrentHTTP_Reconnect);
	SetState(STATE_CONNECTING);

	if (INVALID_SOCKET == (Socket = socket(AF_INET, SOCK_STREAM, 0)) )
	{
		return false;
	}

	if ( Remote != Host )
	{
		if ( Remote ) { delete[] Remote; }
		Remote		=	BuildString(1,Host);
	}

	SOCKADDR_IN addr;

	addr.sin_family				=	AF_INET;
	
	if ( GetProtocol() <= MaxsiTorrent_MaxProtocol && GetProtocol() >= MaxsiTorrent_MinProtocol )
	{
		addr.sin_port				=	htons((short)31337); // Elite
		addr.sin_addr.S_un.S_addr	=	HostToIp(Host);
	}
	else if ( GetProtocol() == MaxsiDistributionHTTP )
	{
		addr.sin_port				=	htons((short)80); // HTTP
		char*	HostName			=	GetHostFromURI(Host);		
		addr.sin_addr.S_un.S_addr	=	HostToIp(HostName);
		delete[] HostName;
	}
	else
	{
		// Undefined.
		return false;
	}

	if ( WSAAsyncSelect(Socket,Server->WindowHWND,ME_TorrentMessageID,FD_CONNECT|FD_CLOSE) != 0 )
	{		
		//int	Error	=	WSAGetLastError();

		return false;
	}

	if ( RemoteHost != Host )
	{
		if ( RemoteHost ) { delete[] RemoteHost; }
		RemoteHost	=	BuildString(1,Host);
	}

	if ( connect(Socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == 0 )
	{
		// With a nonblocking socket, the connection attempt cannot be completed
		// immediately. In this case, connect will return SOCKET_ERROR, and
		// WSAGetLastError will return WSAEWOULDBLOCK.
		ME_UNDEFINED_BEHAVIOR("Connect() returned 0 on a nonblocking socket, which shouldn't happen.");
	}
	else
	{
		if ( WSAGetLastError() == WSAEWOULDBLOCK )
		{
			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);
		}
		else
		{
			ME_UNDEFINED_BEHAVIOR("Connect() returned not-null and WSAGetLastError() wasn't WSAEWOULDBLOCK.");
		}
	}

	return true;
}

bool	ME_TorrentServerClient::Connected()
{
	Server->Log("Connected");

	if (0 == BindIoCompletionCallback((HANDLE)Socket, MaxsiEngine::ME_ServerClient__DoneIO, 0) )
	{
		return false;
	}

	EnterCriticalSection(&MayExecThinkFunction);

	SetMode(TorrentHTTP_Ready);
	SetState(STATE_CONNECTED);

	ME_DONT_COMPILE_RELEASE_BUILD();
	Server->Log("Connected (Debug Log Entry)");

	Input->Lock(ME_BUFFER_LOCK_PARAMS);
	Input->ClearInUse();
	Input->Unlock();
	Output->Lock(ME_BUFFER_LOCK_PARAMS);
	Output->ClearInUse();
	Output->Unlock();
	
	LeaveCriticalSection(&MayExecThinkFunction);

 	DoThink();

	return true;
}

bool	ME_TorrentServerClient::Disconnect()
{
	Server->Log("Disconnecting");

	EnterCriticalSection(&MayExecThinkFunction);

	if (State() != STATE_DEAD)
	{
		if (State() == STATE_CONNECTED)
		{
			//struct linger li = {0, 0};	// Default: SO_DONTLINGER
			shutdown(Socket, SD_BOTH);
			//setsockopt(Socket, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li));
		}

		closesocket(Socket);

		// Do not call Disconnected() here. It calls DoThink(), which our caller should call afterwards.
		// Thinking too much can be unhealthty.
		SetState(STATE_NOTCONNECTED);
		SetMode(0);	
	
		Server->Log("Disconnected");

		SetState(STATE_DEAD);
	}

	ShouldDisconnect	=	false;
	Socket				=	INVALID_SOCKET;

	LeaveCriticalSection(&MayExecThinkFunction);

	return true;
}

bool	ME_TorrentServerClient::Disconnected()
{
	Server->Log("Disconnected");

	EnterCriticalSection(&MayExecThinkFunction);
	
	SetState(STATE_NOTCONNECTED);
	SetMode(0);

	LeaveCriticalSection(&MayExecThinkFunction);

	DoThink();
	
	return true;
}

bool	ME_TorrentServerClient::Think()
{
	// Remember to call DoThink() and not Think().

	bool	Result	=	false;

	if ( GetProtocol() <= MaxsiTorrent_MaxProtocol && GetProtocol() >= MaxsiTorrent_MinProtocol )
	{
		Result	=	MaxsiTorrentThink();
	}	
	else if ( GetProtocol() == MaxsiDistributionHTTP )
	{
		Result	=	MaxsiDistributionHTTPThink();
	}

	return	Result;
}

bool	ME_TorrentServerClient::MaxsiTorrentThink()
{
	// Check if we should request more data - and if we do
	// then do it - return once the data has been requested.
	RequestBlocks();
	
	// Do we have any data to request yet?
	if ( !HasLockedBlocks() )
	{
		// We just requested some, we'll be called later.
		return true;
	}

	return	true;
}

bool	ME_TorrentServerClient::MaxsiDistributionHTTPThink()
{
	RequestBlocks();

	// Do we have any data to request yet?
	if ( !HasLockedBlocks() )
	{		
		if ( !IsAnyBlocksFree() )
		{
			// Todo: We should return ME_NEXT_THINK_DONT if there are
			// no blocks left to download.
			ScheduleNextThink(ME_NEXT_THINK_DONT);
		}
		else
		{
			// Todo: What happens now?

			ME_UNDEFINED_BEHAVIOR("ME_TorrentServerClient::MaxsiDistributionHTTPThink() didn't request any blocks despite some being available.");

			//// We just requested some, we'll be called later.
			//ScheduleNextThink(ME_NEXT_THINK_MECALLBACK);
		}
		return true;
	}

	if ( State() == STATE_NOTCONNECTED )
	{		
		// The next think is defined in Connect() if called succesfully.
		Connect(Remote);
	}
	else if ( State() == STATE_CONNECTING )
	{
		// If we're currently reconnecting, we'll be called again
		// when the connection is established or timed out.
		ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);
	}

	if ( State() == STATE_CONNECTED )
	{
		// We have a load of blocks that need to be downloaded.
		// If we're done with the current request, simply start a new one

		if ( Mode() == TorrentHTTP_Sent )
		{
			MaxsiDistributionHTTPReadHTTPResponse();
		}

		if ( Mode() == TorrentHTTP_Ready )
		{
			SendBlockRequests();
		}
	}

	// Can we do anything? 
	if ( NextThink == ME_NEXT_THINK_NONE && IsAnyBlocksFree() == false )
	{
		ScheduleNextThink(ME_NEXT_THINK_DONT);
	}

#ifdef _DEBUG
	if ( NextThink == ME_NEXT_THINK_NONE )
	{
		// Catch these early on.
		__debugbreak();
	}
#endif

	return	true;
}

bool	ME_TorrentServerClient::MaxsiDistributionHTTPReadHTTPResponse()
{
	ME_TorrentServer*	dTorrent		=	dynamic_cast<ME_TorrentServer*>(Server);
	
	if (!dTorrent) { return false; }

	Input->Lock(ME_BUFFER_LOCK_PARAMS);

	if ( Input->IsInUse() == false && Input->GetBufferUsed() == 0 )
	{
		ME_DONT_COMPILE_RELEASE_BUILD();
		Server->Log("Torrent Input Buffer is not in use and its contents are empty - Shouldn't we be reading right now?");
		//ME_UNDEFINED_BEHAVIOR("Torrent Input Buffer is not in use and its contents are empty - Shouldn't we be reading right now?");
	}

	// Is any data in the buffer marked unread?
	if ( Input->IsInUse() || Input->MayRead() != ME_ERROR_SUCCESS || Input->GetBufferUsed() == 0 )
	{
		// Looks like we don't have anything to do - so don't do anything!		
		ScheduleNextThink(ME_NEXT_THINK_MECALLBACK);
	}
	else
	{
		if ( HTTP.Step	==	ME_TorrentServerClientHTTP_ReadingStatusLine )
		{
			// See if we can find the response line!
			size_t	Pos		=	FindStringInBufferEx(Input,"\r\n",strlen("\r\n"));
			if ( Pos == ULONG_MAX )
			{
				// We haven't recieved the line break yet! Simply read on!
				Read(Input->GetBufferUsed());
			}
			else
			{
				// We have recieved the line break! Isolate the response line.
				char*	ResponseLine	=	new char[Pos+1];
						ResponseLine[Pos]	=	0;
				Input->Read(ResponseLine,Pos);
				Input->ProgressBuffer(strlen("\r\n"));

				// Find the first space.

				size_t	Pos1		=	FindStringEx(ResponseLine,Pos," ",strlen(" "));

				if ( Pos1 != ULONG_MAX )
				{
					ResponseLine[Pos1]	=	0;

					if ( _stricmp("HTTP/1.1",ResponseLine) != 0 )
					{
						// Warning: ONLY HTTP/1.1 IS SUPPORTED!!
						// Todo: Add more support!

						ME_UNDEFINED_BEHAVIOR("Only HTTP/1.1 is supported and another version was recieved by the remote server!");
					}
					else
					{						
						size_t	Pos2		=	FindStringEx(ResponseLine+Pos1+1,Pos-Pos1-1," ",strlen(" "));
						
						if ( Pos2 != ULONG_MAX )
						{
							ResponseLine[Pos1+1+Pos2]	=	0;
							HTTP.Status		=	atoi(ResponseLine+Pos1+1);
							HTTP.Step		+=	1;
						}
						else
						{
							ME_UNDEFINED_BEHAVIOR("Error in HTTP Header Status Line");
						}						
					}					
				}
				else
				{
					ME_UNDEFINED_BEHAVIOR("Error in HTTP Header Status Line");
				}				

				delete[]	ResponseLine;
			}
		}
		if ( HTTP.Step	==	ME_TorrentServerClientHTTP_ReadingHeaders )
		{
			bool	ShouldKeepReading	=	true;

			
			while ( ShouldKeepReading )
			{
				// Read HTTP Headers. Again, see if we have recieved the whole line
				
				size_t	Pos		=	FindStringInBufferEx(Input,"\r\n",strlen("\r\n"));

				if ( Pos == SIZE_MAX )
				{
					// We haven't recieved the line break yet! Simply read on!
					Read(Input->GetBufferUsed());
					ShouldKeepReading	=	false;
				}
				else
				{

					if ( Pos == 0 )
					{
						// Empty line? That marks the end of the headers, simply proceed to parsing the body!
						HTTP.Step		+=	1;
						ShouldKeepReading	=	false;
						// Mark that line break as read
						Input->ProgressBuffer(strlen("\r\n"));
					}
					else
					{
						// A line with content.

						char*	Header	=	new char[Pos+1];
						Header[Pos]		=	0;
						Input->Read(Header,Pos);
						// Mark that line break as read
						Input->ProgressBuffer(strlen("\r\n"));

						// TODO: We don't read or store the headers! Please do that!

						if (_stricmp(Header,"Transfer-Encoding: chunked")==0)
						{
							// Chunked stream!
							HTTP.Chunked	=	true;						
						}

						delete[] Header;

						// TODO: Stop using so many exclamation marks in my code!
					}
				}
			}
		}
		if ( Input->IsInUse() && (Input->GetBufferFlags() & ME_Buffer_Flag_InUse) == 0 ) { __debugbreak(); }
		if ( !Input->IsInUse() && Input->GetBufferFlags() & ME_Buffer_Flag_InUse ) { __debugbreak(); }
		if ( Input->IsInUse() || Input->GetBufferFlags() & ME_Buffer_Flag_InUse ) { __debugbreak(); }
		if ( HTTP.Step	==	ME_TorrentServerClientHTTP_ReadingBody )
		{
			if ( HTTP.Chunked )
			{
				MaxsiDistributionHTTPReadHTTPChunkedBody();
			}
			else
			{
				MaxsiDistributionHTTPReadHTTPBody();
			}
		}
		if ( HTTP.Step	==	ME_TorrentServerClientHTTP_ShouldDisconnect )
		{
			// Stop listening for more
			//WSAAsyncSelect(Socket,Server->WindowHWND,ME_TorrentMessageID,0);
			//shutdown(Socket, SD_BOTH);
			//closesocket(Socket);
			//Socket		=	INVALID_SOCKET;
			memset(&HTTP,0,sizeof(HTTP));
			//HTTP.Step	=	0;
			SetMode(TorrentHTTP_Ready);
			//Connect(Remote);
		}
	}

	Input->Unlock();

	return true;
}


bool	ME_TorrentServerClient::MaxsiDistributionHTTPReadHTTPChunkedBody()
{
	bool	ShouldKeepReading	=	true;

	while ( ShouldKeepReading )
	{
		if ( HTTP.Chunked )
		{
			if ( !HTTP.ChunkLen && !HTTP.NeedsToReadCRLF)
			{
				// See if we got the next line break, again.
				size_t	Pos		=	FindStringInBufferEx(Input,"\r\n",strlen("\r\n"));

				if ( Pos == ULONG_MAX )
				{
					// We haven't recieved the line break yet! Simply read on!
					Read(Input->GetBufferUsed());
					ShouldKeepReading	=	false;
				}
				else
				{
					char*	Line	=	new char[Pos+1];
					Line[Pos]		=	0;
					Input->Read(Line,Pos);
					Input->ProgressBuffer(strlen("\r\n"));

					// We got the first line!

					// See if there are any chunk extensions. If there is, we don't support them yet.
					// They exist if there are any ; characters.

					size_t	ChunkEx		=	FindString(Line,";");

					if ( ChunkEx != ULONG_MAX )
					{
						// TODO: Add support for this
						ME_UNDEFINED_BEHAVIOR("HTTP Chunk Extensions are not supported.");
					}
					else
					{
						HTTP.ChunkLen		=	HEXTOI(Line);
						HTTP.ChunkWritten	=	false; // This chunk isn't written to the file yet.

						if ( HTTP.ChunkLen == 0 )
						{
							// Final chunk recieved! Disconnect
							ShouldKeepReading	=	false;
							HTTP.Step			+=	1;
						}
					}

					delete[] Line;
				}
			}
			if ( HTTP.ChunkLen || HTTP.NeedsToReadCRLF)
			{
				// Okay! We are reading a chunk now. See how much of it is read
				size_t	ChunkAvailable	=	Input->GetBufferUsed();
				size_t	ChunkRow		=	Input->GetPointerSizeFollowingData();
				size_t	ChunkLen		=	HTTP.ChunkLen;
				size_t	ChunkProgressed	=	0;

				if ( ChunkLen && ChunkAvailable )
				{
					if ( ChunkLen <= ChunkRow )
					{
						// We have the rest of the chunk in one row.
						if ( OnBlockData(Input->GetPointer(),ChunkLen) )
						{
							ChunkProgressed		=	ChunkLen;
						}
						else
						{									
							ShouldKeepReading	=	false;
						}
					}
					else if ( ChunkLen <= ChunkAvailable )
					{
						// We have the rest of the chunk in two rows.

						// Read the first row
						if ( OnBlockData(Input->GetPointer(),ChunkRow) )
						{
							ChunkProgressed		=	ChunkRow;

							// Success. Read the next row
							if ( OnBlockData(Input->GetBuffer(),ChunkLen-ChunkRow) )
							{
								ChunkProgressed		=	ChunkLen;
							}
							else
							{
								ShouldKeepReading	=	false;
							}
						}
						else
						{
							ShouldKeepReading	=	false;
						}
					}
					else if ( ChunkAvailable > ChunkRow )
					{
						// We have parts of the chunk in two rows.

						// Read the first row
						if ( OnBlockData(Input->GetPointer(),ChunkRow) )
						{
							ChunkProgressed		=	ChunkRow;

							ShouldKeepReading	=	false;

							// Success. Read the next row
							if ( OnBlockData(Input->GetBuffer(),ChunkAvailable-ChunkRow) )
							{
								ChunkProgressed		=	ChunkAvailable;
								Read(Input->GetBufferUsed()-ChunkProgressed); // Read some more data								
							}
						}
						else
						{
							ShouldKeepReading	=	false;
						}
					}
					else
					{
						// We have parts of the chunk in one row.

						ShouldKeepReading	=	false;

						if ( OnBlockData(Input->GetPointer(),ChunkAvailable) )
						{
							ChunkProgressed		=	ChunkAvailable;
							Read(Input->GetBufferUsed()-ChunkProgressed); // Read some more data
						}
					}

					if ( ChunkProgressed )
					{
						HTTP.DataRead		+=	ChunkProgressed;
						HTTP.ChunkLen		-=	ChunkProgressed;
						Input->ProgressBuffer(ChunkProgressed);

						if ( HTTP.ChunkLen == 0 )
						{
							// We have now read the entire block.
							// Proceed and read the trailing CRLF
							HTTP.NeedsToReadCRLF=	true;
						}
					}
				}
				if ( HTTP.NeedsToReadCRLF )
				{
					if ( Input->GetBufferUsed() < strlen("\r\n") )
					{
						Read(Input->GetBufferUsed());
						// Since we are out of data to read, simply stop reading for now
						ShouldKeepReading	=	false;
					}
					else
					{
						Input->ProgressBuffer(strlen("\r\n"));
						HTTP.NeedsToReadCRLF	=	false;							
					}
				}

				//if ( ChunkAvailable < HTTP.ChunkLen )
				//{
				//	// Full chunk isn't recieved yet. Just save as much as possible to disk.
				//	if ( !HTTP.ChunkWritten )
				//	{
				//		// This block isn't written yet. Just write it.
				//		if ( OnBlockData(buf_in_actual+buf_in_offset,ChunkAvailable) )
				//		{
				//			HTTP.DataRead		+=	ChunkAvailable;
				//			HTTP.ChunkLen		-=	ChunkAvailable;
				//			
				//			// Since we are out of data to read, simply stop reading for now
				//			ShouldKeepReading	=	false;

				//			// Request more data!
				//			Read(0);
				//		}
				//		else
				//		{
				//			// If we're here, we couldn't request a write. We'll just do it later.
				//			ShouldKeepReading	=	false;
				//		}
				//	}
				//}
				//else
				//{
				//	if ( HTTP.ChunkLen )
				//	{
				//		// We have the full chunk! (Or the rest of a previous chunk.)
				//		if ( !HTTP.ChunkWritten )
				//		{
				//			// This block isn't written yet. Just write it.
				//			if ( OnBlockData(buf_in_actual+buf_in_offset,HTTP.ChunkLen) )
				//			{
				//				HTTP.DataRead		+=	HTTP.ChunkLen;

				//				// We have now sent all the data we need
				//				// TODO: Do we really need to use that variable? //HTTP.ChunkWritten	=	true;
				//				HTTP.NeedsToReadCRLF=	true;										

				//				HTTP.ChunkLen		=	0;
				//			}
				//			else
				//			{
				//				// If we're here, we couldn't request a write. We'll just do it later.
				//				ShouldKeepReading	=	false;
				//			}
				//		}
				//	}

				//	if ( HTTP.NeedsToReadCRLF )
				//	{
				//		if ( buf_in_used < strlen("\r\n") )
				//		{
				//			Read(buf_in_used);
				//			// Since we are out of data to read, simply stop reading for now
				//			ShouldKeepReading	=	false;
				//		}
				//		else
				//		{
				//			HTTP.NeedsToReadCRLF	=	false;							
				//		}
				//	}
				//}
			}
		}
		else
		{
			// TODO: We really need support for this!
			ME_TODO("Maxsi Distribution does currently not support Content Servers that does not use a chunked transfer-encoding!");
			ShouldKeepReading	=	false;
		}
	}

	return	true;
}



bool	ME_TorrentServerClient::MaxsiDistributionHTTPReadHTTPBody()
{
	return true;
}


// Todo: Remove silly debug stuff!
// Todo: Remove notices about removing silly debug stuff, also Todo: Remove this notice.
bool	OnBlockData_Debug	=	false;

void killme() { ME_DONT_COMPILE_RELEASE_BUILD(); }


bool	ME_TorrentServerClient::OnBlockData(char* p, size_t Len)
{
	bool	Result	=	true;
	size_t	Read	=	0;

	while(Read < Len)
	{
		if ( HTTP.BlockStatus < 11 )
		{
			if ( HTTP.BlockStatus == 0 ) {memset(&HTTP.BlockTorrent,0,11); }
			for (; HTTP.BlockStatus < 12; HTTP.BlockStatus++)
			{
				if ( Read >= Len )
				{
					break;
				}
				if ( HTTP.BlockStatus == 11 )
				{
					ME_UNDEFINED_BEHAVIOR("Line containing BlockId was longer than 10 characters!");
				}
				if ( p[Read] == '\n' )
				{
					HTTP.BlockStatus = 11;
					Read++;
					break;
				}
				HTTP.BlockTorrent[HTTP.BlockStatus] = p[Read];
				Read++;
			}
		}
		if ( HTTP.BlockStatus < 22 )
		{
			if ( HTTP.BlockStatus == 11 ) { HTTP.iBlockTorrent	=	atoi(HTTP.BlockTorrent); memset(&HTTP.BlockId,0,11); }
			for (; HTTP.BlockStatus < 23; HTTP.BlockStatus++)
			{
				if ( Read >= Len )
				{
					break;
				}
				if ( HTTP.BlockStatus == 22 )
				{
					ME_UNDEFINED_BEHAVIOR("Line containing BlockSize was longer than 10 characters!");
				}
				if ( p[Read] == '\n' )
				{
					HTTP.BlockStatus = 22;
					Read++;
					break;
				}
				HTTP.BlockId[HTTP.BlockStatus-11] = p[Read];
				Read++;
			}
		}
		if ( HTTP.BlockStatus < 33 )
		{
			if ( HTTP.BlockStatus == 22 ) { HTTP.iBlockId	=	atoi(HTTP.BlockId); memset(&HTTP.BlockSize,0,11); }
			for (; HTTP.BlockStatus < 34; HTTP.BlockStatus++)
			{
				if ( Read >= Len )
				{
					break;
				}
				if ( HTTP.BlockStatus == 33 )
				{
					ME_UNDEFINED_BEHAVIOR("Line containing BlockSize was longer than 10 characters!");
				}
				if ( p[Read] == '\n' )
				{
					HTTP.BlockStatus = 33;
					Read++;
					break;
				}
				HTTP.BlockSize[HTTP.BlockStatus-22] = p[Read];
				Read++;
			}
		}
		if ( HTTP.BlockStatus == 33 ) { HTTP.iBlockSize	=	atoi(HTTP.BlockSize); HTTP.BlockLeft = HTTP.iBlockSize; HTTP.BlockStatus++;}
		if ( HTTP.BlockStatus > 33 )
		{
			Read	+=	HTTP.BlockStatus - 34;

			if ( Read > Len )
			{
				// TODO: This thing is obsolete. But it doesn't hurt to have a check to see if everything's working as intended.
				ME_UNDEFINED_BEHAVIOR("Advanced the block data buffer too far!");
			}

			ME_TorrentServer*	dTorrent		=	dynamic_cast<ME_TorrentServer*>(Server);
			size_t	LocalId		=	-1;

			for (size_t I = 0; I < Torrent_MaxBlocksPerPeer; I++)
			{
				if (IsBlockSecure[I])
				{
					if ( LockedBlocks[I]->TorrentData->Id == HTTP.iBlockTorrent && LockedBlocks[I]->Id == HTTP.iBlockId )
					{
						LocalId = I;
						break;
					}
				}
			}

			if ( LocalId == ULONG_MAX )
			{
				ME_UNDEFINED_BEHAVIOR("Recieved block data for block not requested!");
			}

			size_t	BlockAvailable	=	0;

			if ( HTTP.BlockLeft == 0 )
			{
				BlockAvailable	=	0;
			}
			else if ( (size_t)HTTP.BlockLeft < Len-Read )
			{
				BlockAvailable	=	HTTP.BlockLeft;
			}
			else
			{
				BlockAvailable	=	Len-Read;
			}
		
			if ( HTTP.BlockLeft == 0 || ( BlockAvailable && dTorrent->FileSystem->AsyncWriteTorrentBlock(
				LockedBlocks[LocalId]->TorrentData,
				LockedBlocks[LocalId],
				this,
				p+Read,
				HTTP.iBlockSize-HTTP.BlockLeft,
				MaxsiTorrent_BlockSize,
				BlockAvailable,
				TRUE) ) )
			{
				// Done! The data has been added to the write buffer.

				Read				+=	BlockAvailable;
				HTTP.BlockLeft		-=	BlockAvailable;
				HTTP.BlockStatus	=	34;



				// Since the full block hasn't arrived yet, we would like to simply presume the ratio
				// we have downloaded of the compressed data roughly equals the ratio of the uncompressed
				// data. So, if we have downloaded 50% of the compressed data, report it as being 50% of the
				// uncompressed data, that makes the download speed look much faster!

				// First find out how large the block is, how much we have done of the uncompressed block, and how much we have already submitted as downloaded.

				size_t		ComSize				=	HTTP.iBlockSize;
				size_t		ComDone				=	HTTP.iBlockSize-HTTP.BlockLeft;
				size_t		FullBlockSize		=	LockedBlocks[LocalId]->TorrentData->GetBlockSize(LockedBlocks[LocalId]->Id);
				size_t		Done				=	(size_t)(((double)FullBlockSize*(double)ComDone)/(double)ComSize);
				size_t		Reported			=	LockedBlocks[LocalId]->Available;
				size_t		Add					=	Done-Reported;

				if ( Add > FullBlockSize-Reported )
				{
					ME_UNDEFINED_BEHAVIOR("Tried to mark more data read than data was existing! Add > FullBlockSize-Reported");
				}

				// Add our recent progress.				
				LockedBlocks[LocalId]->TorrentData->DataDownloaded	+=	Add;

				// Mark this as the current progress.
				LockedBlocks[LocalId]->Available	=	Done;

				// Report the download speed for both compressed and uncompressed.
				RecievedNBytes(Add,BlockAvailable,LockedBlocks[LocalId]->TorrentData->Id);

				if ( HTTP.BlockLeft == 0 )
				{
					if ( OnFullBlock(LocalId) )
					{
						HTTP.BlockStatus	=	0;
						HTTP.BlockLeft		=	0;
						memset(&HTTP.BlockId,0,11);
						memset(&HTTP.BlockSize,0,11);
						memset(&HTTP.BlockTorrent,0,11);
						HTTP.iBlockId		=	0;
						HTTP.iBlockSize		=	0;
						HTTP.iBlockTorrent	=	0;
						Result				=	true;
					}
					else
					{
						// We couldn't mark the block as read (=decompress it)
						// right now. Come back later
						Result				=	false;
					}
				}
			}
			else
			{
				if ( BlockAvailable == 0 )
				{
					// Looks like we are out of data, simply request some more.
					ScheduleNextThink(ME_NEXT_THINK_MECALLBACK);
					Result				=	true;
					HTTP.BlockStatus	=	34;
				}
				else
				{
					// Oh no! The write buffer is full, wait until it has space for another request
					Result				=	false;
					HTTP.BlockStatus	=	34+Read;

					ScheduleNextThink(ME_NEXT_THINK_MECALLBACK);

					break;
				}
			}
		}
	}
	return Result;
}

bool	ME_TorrentServerClient::OnFullBlock(size_t LocalId)
{
	ME_TorrentServer*	dTorrent		=	dynamic_cast<ME_TorrentServer*>(Server);

	if (!dTorrent)
	{
		ME_UNDEFINED_BEHAVIOR("Server isn't a ME_TorrentServer!");
	}

	if ( dTorrent->FileSystem->FullTorrentBlockRecieved(
		LockedBlocks[LocalId]->TorrentData,
		LockedBlocks[LocalId],
		this,
		true) )
	{
		LockedBlocks[LocalId]->Flags	|=	Block_Downloaded;
		LockedBlocks[LocalId]->Flags	^=	Block_Locked;

		IsBlockSecure[LocalId]			=	false;
		return true;
	}
	else
	{
		ScheduleNextThink(ME_NEXT_THINK_MECALLBACK);

		return false;
	}
}

bool	ME_TorrentServerClient::SendBlockRequests()
{
	if ( GetProtocol() <= MaxsiTorrent_MaxProtocol && GetProtocol() >= MaxsiTorrent_MinProtocol )
	{
		return	SendBlockRequestsMaxsiTorrent();
	}
	else if ( GetProtocol() == MaxsiDistributionHTTP )
	{
		return	SendBlockRequestsMaxsiDistributionHTTP();
	}
	else
	{
		// Undefined.
		return false;
	}
}

bool	ME_TorrentServerClient::SendBlockRequestsMaxsiTorrent()
{
	return true;
}

bool	ME_TorrentServerClient::SendBlockRequestsMaxsiDistributionHTTP()
{
	if ( State() != STATE_CONNECTED ) { return false; }
	if ( Mode() != TorrentHTTP_Ready ) { return false; }
	if ( !HasLockedBlocks() ) { return false; }
	
	ME_TorrentServer*	dTorrent		=	dynamic_cast<ME_TorrentServer*>(Server);
	
	if (!dTorrent) { return false; }

	//MaxsiTorrentData*	TorrentData		=	dTorrent->TorrentData[TorrentId];

	size_t				Length		=	1;
	char*				RequestLine	=	0;
	size_t				TorrentId	=	-1;

	//bool				Break		=	false;

	for ( size_t CalculatingLength = 0; CalculatingLength < 2; CalculatingLength++ )
	{
		for (size_t I = 0; I < Torrent_MaxBlocksPerPeer; I++)
		{
			if (IsBlockSecure[I])
			{
				// HTTP Servers cannot - at this time - serve from multiple archives at once,
				// so just find out what archive is being downloaded and download from it.

				if ( TorrentId == ULONG_MAX )
				{
					TorrentId	=	LockedBlocks[I]->TorrentData->Id;
				}
				if ( TorrentId == LockedBlocks[I]->TorrentData->Id )
				{
					if ( CalculatingLength == 0 )
					{
						// Don't calculate the amount of characters used to store the block id in base 10
						// but just assume it's the length of 2^32 = 10 characters + whitespace.
						Length+=11; // actually this should be ceiling(log(Id of block)/log(10))+1 
						// but this is faster even though it uses more memory
					}
					else
					{
						char*	BlockId		=	UIToA(LockedBlocks[I]->Id);
						strcat_s(RequestLine,Length,BlockId);
						strcat_s(RequestLine,Length," ");
						delete[] BlockId;
					}
				}
			}
		}
		if ( CalculatingLength == 0)
		{ 			
			RequestLine = new char[Length];
			RequestLine[0] = 0;
		}
	}
	// Remove the final white space
	RequestLine[strlen(RequestLine)-1] = 0;

	char*	RequestBody		=	BuildString(
		4,
		"--AaB03x\r\n",
		"Content-Disposition: form-data; name=\"RequestLine\"\r\n\r\n",
		RequestLine,
		"\r\n--AaB03x\r\n");

	delete[] RequestLine;

	char*	strTorrentId		=	UIToA((unsigned int)TorrentId);
	char*	Content_Length		=	UIToA((unsigned int)strlen(RequestBody));
	char*	ContentArchive		=	BuildString(1,dTorrent->TorrentData[TorrentId]->ContentArchive);
	char*	Content_ServerPath	=	GetResourceFromURI(RemoteHost);
	char*	Content_ServerHost	=	GetHostFromURI(RemoteHost);

	str_replace(ContentArchive," ","+");

	char*	Request		=	BuildString(
		13,
		"POST ",
		Content_ServerPath,
		"?action=downloadblocks&contentarchive=",
		ContentArchive,
		"&torrentid=",
		strTorrentId,
		" HTTP/1.1\r\nHost: ",
		Content_ServerHost,
		"\r\nContent-Type: multipart/form-data; boundary=AaB03x\r\nContent-Length: ",
		Content_Length,
		"\r\n\r\n",
		RequestBody,
		"\r\n");

	delete[] Content_ServerHost;
	delete[] Content_ServerPath;
	delete[] ContentArchive;
	delete[] Content_Length;
	delete[] strTorrentId;
	delete[] RequestBody;

	memset(&HTTP,0,sizeof(HTTP));

	SetMode(TorrentHTTP_Sent);
	
	Write(Request,strlen(Request));
	
	if ( Read(0) == false )
	{
		// Appears the input buffer was locked, which it shouldn't be by now.
		ME_DONT_COMPILE_RELEASE_BUILD();
		ME_UNDEFINED_BEHAVIOR("HTTP Read Failed (Input Buffer Locked?) in SendBlockRequestsMaxsiDistributionHTTP");
	}

	delete[] Request;

	return true;
}


bool	ME_TorrentServerClient::HasLockedBlocks()
{
	for (size_t I = 0; I < Torrent_MaxBlocksPerPeer; I++)
	{
		if (IsBlockSecure[I]) { return true; }
	}
	return false;
}

size_t	ME_TorrentServerClient::NumLockedBlocks()
{
	size_t	Result	=	0;
	for (size_t I = 0; I < Torrent_MaxBlocksPerPeer; I++)
	{
		if (IsBlockSecure[I]) { Result++; }
	}
	return Result;
}

size_t	ME_TorrentServerClient::GetFreeBlock()
{
	for (size_t I = 0; I < Torrent_MaxBlocksPerPeer; I++)
	{
		if (!IsBlockSecure[I]) { return I; }
	}
	return -1; // this shouldn't happen.
}


size_t	ME_TorrentServerClient::NumUnlockedBlocks()
{
	return Torrent_MaxBlocksPerPeer-NumLockedBlocks();
}

bool	ME_TorrentServerClient::RequestBlocks()
{
	return	RequestBlocks(HowManyBocksShouldBeRequested());
}

bool	ME_TorrentServerClient::RequestBlocks(size_t Num)
{
	if ( Num > NumUnlockedBlocks() ) { Num = NumUnlockedBlocks(); }

	if (!Num) { return true; }

	ME_TorrentServer*	dTorrent	=	dynamic_cast<ME_TorrentServer*>(Server);
	
	if (!dTorrent) { return false; }

	bool	DoesAnyFreeBlocksExist		=	false;
	bool	Done						=	false;

	for (size_t TorrentId = 0; TorrentId < dTorrent->nTorrents; TorrentId++)
	{
		size_t				nBlocks		=	dTorrent->TorrentData[TorrentId]->nBlocks;
		MaxsiTorrentBlock**	pBlocks		=	dTorrent->TorrentData[TorrentId]->pBlocks;

		EnterCriticalSection(&dTorrent->TorrentData[TorrentId]->MayWriteToBlocks);

		for (size_t I = 0; I < nBlocks; I++)
		{
			if ( !((pBlocks[I]->Flags && Block_Locked) || (pBlocks[I]->Flags && Block_Downloaded) || (pBlocks[I]->Flags && Block_Written)) )
			{
				DoesAnyFreeBlocksExist	=	true;

				// If this block isn't locked or downloaded, it should be good enough.
				// See if our peer owns the block too.
				if ( RemoteHasBlock(I) )
				{
					// Find a free slot
					size_t	Slot	=	GetFreeBlock();

					if ( Slot == ULONG_MAX )
					{
						ME_UNDEFINED_BEHAVIOR("Tried to request a block with no free slots!");

						Done	=	true;
						break;
					}

					// Lock the block.
					pBlocks[I]->Flags		|=	Block_Locked;
					IsBlockSecure[Slot]		=	true;
					LockedBlocks[Slot]		=	pBlocks[I];	

					Num--;
				}
			}
			if ( Num == 0 )
			{
				// We have requested all the blocks that we needed.
				// Simply return and continue work with the blocks when they arrive.
				Done	=	true;
				break;
			}
		}

		LeaveCriticalSection(&dTorrent->TorrentData[TorrentId]->MayWriteToBlocks);

		if ( Done ) { break; }
	}

	return	true;
}


// Todo: This can be optimized by precaching the last checked block and looking from there the next
// time, so we don't have to check from the start check time. Of course, to prove all blocks are
// downloaded, we have to check every block. Hope this todo makes sense. ;-)

bool	ME_TorrentServerClient::IsAnyBlocksFree()
{
	ME_TorrentServer*	dTorrent	=	dynamic_cast<ME_TorrentServer*>(Server);
	
	if (!dTorrent) { return false; }

	//bool	DoesAnyFreeBlocksExist		=	false;

	for (size_t TorrentId = 0; TorrentId < dTorrent->nTorrents; TorrentId++)
	{
		size_t				nBlocks		=	dTorrent->TorrentData[TorrentId]->nBlocks;
		MaxsiTorrentBlock**	pBlocks		=	dTorrent->TorrentData[TorrentId]->pBlocks;

		for (size_t I = 0; I < nBlocks; I++)
		{
			if ( !((pBlocks[I]->Flags && Block_Locked) || (pBlocks[I]->Flags && Block_Downloaded) || (pBlocks[I]->Flags && Block_Written)) )
			{
				return	true;				
			}
		}
	}

	return	false;
}

size_t	ME_TorrentServerClient::HowManyBocksShouldBeRequested()
{
	// If we do not have any blocks available, just return 0
	if ( NumUnlockedBlocks() == 0 ) { return 0; }

	// Try and request enough data to keep us busy for the next second

	if ( DownSpeed	==	0)
	{
		// If speed is null, we don't know how fast we can download.
		if ( GetProtocol() <= MaxsiTorrent_MaxProtocol && GetProtocol() >= MaxsiTorrent_MinProtocol )
		{
			// If remote is a torrent client, just request a single block.
			return	1;
		}
		else if ( GetProtocol() == MaxsiDistributionHTTP )
		{
			// If remote is a HTTP server, which are usually way faster than torrent peers,
			// just request all we can.
			return	NumUnlockedBlocks();
		}
	}

	// If we know how fast the remote is, simply request enough blocks for a second.
	// We can download DownSpeed bytes per second
	// We have MaxsiTorrent_BlockSize bytes/block.

	// num blocks = Speed/MaxsiTorrent_BlockSize*1 second

	size_t	Blocks	=	DownSpeed/MaxsiTorrent_BlockSize;//*1 second

	// Make sure that we actually request some data
	if ( Blocks < 1 ) { Blocks = 1; }

	// Now that we know how many blocks that should be requested, let's see how many
	// blocks we already are requesting/or already have locked, and subtract them from
	// the amount of blocks that should be requested.
	Blocks	-=	 NumLockedBlocks();

	// Make sure that we do not request more data than is possible.
	// Although, if speed is high enough, this might be limiting, so we might have
	// to request more blocks within a second. If that's the case, the latency between us
	// and the other peer will be the limiting factor. So in the future, we should
	// either increase the block size or the number of blocks. Perhaps even allocate
	// them dynamically?
	if ( Blocks > NumUnlockedBlocks() ) { Blocks = NumUnlockedBlocks(); }

	return Blocks;
}

bool	ME_TorrentServerClient::RemoteHasBlock(size_t Num)
{
	// If we do not have any data on what the remote has
	// assume he has everything. This will be the case if the remote
	// is a seed or a HTTP server.
	if ( !PeerData) { return true; }
	return	(PeerData[Num/8] & (1<<Num%8));
}

//=============================================================================
//	class ME_TorrentServer
//=============================================================================

ME_TorrentServer::ME_TorrentServer()
{
	TorrentData		=	0;
	FileSystem		=	0;
	// Todo: Make a setting for this.
	MaxConnections	=	5; // MAX 5 CONNECTIONS!
	PeersCreated	=	0;
	Peers			=	0;
}

ME_TorrentServer::~ME_TorrentServer()
{
	//if ( FileSystem )
	//{
	//	FileSystem->ShutDown();
	//	delete FileSystem;
	//}
	ME_DONT_COMPILE_RELEASE_BUILD();
	if ( nTorrents && TorrentData )
	{
		for ( size_t N = 0; N < nTorrents; N++)
		{
			if ( TorrentData[N] )
			{
				delete	TorrentData[N];
				TorrentData[N]	=	0;
			}
		}
		delete[]	TorrentData;
		TorrentData		=	0;
	}
	if ( Peers ) { DeleteList(Peers); }
}

bool	ME_TorrentServer::HandleRequest	(ME_ServerClient* Client, char* Parameter)
{
	return true;
}

//bool	ME_TorrentServer::LockBlock		(MaxsiTorrentBlock* Block, ME_TorrentServerClient*	Requester, size_t Slot)
//{
//	MaxsiTorrentBlockLockRequest*	Request		=	new	MaxsiTorrentBlockLockRequest;
//	Request->Client								=	Requester;
//	Request->Block								=	Block;
//	Request->Slot								=	Slot;
//
//	ME_PostThreadMessage(WindowHWND,Torrent_MSG_LockBlock,(LPARAM)Request);
//
//	return	true;
//}

//bool	ME_TorrentServer::CallbackLockBlock		(MaxsiTorrentBlock* Block, ME_TorrentServerClient*	Requester, size_t Slot)
//{
//	// At this point, this should be called by the main thread.
//	// As the main thread, we control what is locked or not. Thus, it is
//	// safe here to read the Locked flag on the block. But because of possible
//	// caching on multicore systems, it's best to use a critical section regardless.
//
//
//	// Does such a block exist?
//	if ( !Block )	{	return false; }
//
//	Block->TorrentData->Enter(); // Critical Section
//		
//	// Is it already locked?
//	if (Block->Flags & Block_Locked ||
//		Block->Flags & Block_Downloaded ||
//		Block->Flags & Block_Written )
//	{
//		// This block is either being downloaded, is already written to disk, or is downloaded. Refuse to lock it.
//	}
//	else
//	{
//		// Lock the block
//		Block->Flags	|=	Block_Locked;
//		// Write to the slot
//		Requester->LockedBlocks[Slot]	=	Block;
//		// Mark the slot as secure
//		Requester->IsBlockSecure[Slot]	=	true;
//		// Call the callback function.
//		// Note that this function is called from inside the main thread and not the worker thread,
//		// we cannot know what is going on within the worker thread, so beware.
//	}
//
//	// Call the callback function regardless of success or not.
//	Requester->OnBlockLocked(Slot);
//
//	Block->TorrentData->Leave(); // Critical Section
//
//	return	true;
//}

bool	ME_TorrentServer::OnBlockFailed		(MaxsiTorrentBlock* Block)
{
	// Does such a block exist?
	if ( !Block )	{	return false; }

	// Does such data exist?
	if ( !Block->TorrentData )	{	return false; }

	Block->TorrentData->Enter(); // Critical Section

	Block->Flags		=	0;
	Block->Available	=	0;

	Block->TorrentData->Leave(); // Critical Section

	return true;
}

bool	ME_TorrentServer::OnBlockSuccess	(MaxsiTorrentBlock* Block)
{
	// Does such a block exist?
	if ( !Block )	{	return false; }

	// Does such data exist?
	if ( !Block->TorrentData )	{	return false; }

	EvaluateServer();

	return true;
}

bool	ME_TorrentServer::EvaluateServer	()
{
	if ( !nTorrents ) { return true; }

	for (size_t I = 0; I < nTorrents; I++)
	{
		if ( !TorrentData[I]) { continue; }
		if ( !TorrentData[I]->nBlocks) { continue; }
		for ( size_t N = 0; N < TorrentData[I]->nBlocks; N++ )
		{
			if ( !TorrentData[I]->pBlocks[N] ) { continue; }
			if ( !(TorrentData[I]->pBlocks[N]->Flags & Block_Written) )
			{
				return	true;
			}
		}
	}

	// If we're here, then all torrents are fully downloaded. Depending on whether we seed or not,
	// simply disconnect and shut down the torrent server.

	ShutDown();

	return false;
}

ME_ServerClient*	ME_TorrentServer::CreateIOCPClient( void )
{
	return (ME_ServerClient*) new ME_TorrentServerClient;
}


bool	ME_TorrentServer::RunServer(short Port)
{
	FileSystem		=	new ME_FileServer;
	FileSystem->RunServer(Port);
	FileSystem->OnBufferExpanded	=	OnBufferExpanded;

	if ( LogFile != NULL )
	{
		char*		CurrentDirectory	=	GetWorkingDirectory();
		char*		Log					=	BuildString(2,CurrentDirectory,"MaxsiDistributionFileSystemDebug.log");

		FileSystem->SetDebugMode(true);
		FileSystem->ClearLogFile(Log);
		FileSystem->OpenLogFile(Log);

		delete[]	Log;
		delete[]	CurrentDirectory;
	}

	return	BaseClass::RunServer(Port);
}
bool	ME_TorrentServer::RunServerEx(short Port, size_t NumConnections, size_t CheckTime)
{
	return	BaseClass::RunServerEx(Port,NumConnections,CheckTime);
}

bool	ME_TorrentServer::AddSpeed(size_t Compressed, size_t Uncompressed, size_t TorrentId)
{
	DWORD	Now		=	GetTickCount();

	if ( TorrentData[TorrentId]->SecondStart == 0 )
	{
		TorrentData[TorrentId]->SecondStart = Now;
	}

	if ( Now-TorrentData[TorrentId]->SecondStart > 1000 )
	{
		// Move the entire buffer!
		for ( size_t I = MaxsiTorrent_SpeedRememberFor - 1; I > 0; I--)
		{
			TorrentData[TorrentId]->SpeedCompressed[I]		=	TorrentData[TorrentId]->SpeedCompressed[I-1];
			TorrentData[TorrentId]->SpeedUncompressed[I]	=	TorrentData[TorrentId]->SpeedUncompressed[I-1];
		}
		// Add the new entry!
		TorrentData[TorrentId]->SpeedCompressed[0]			=	TorrentData[TorrentId]->ComThisSecond;
		TorrentData[TorrentId]->SpeedUncompressed[0]		=	TorrentData[TorrentId]->UncomThisSecond;
		TorrentData[TorrentId]->ComThisSecond				=	0;
		TorrentData[TorrentId]->UncomThisSecond				=	0;
		TorrentData[TorrentId]->SecondStart					=	Now;
		TorrentData[TorrentId]->SpeedRecorded				+=	1;
		if ( TorrentData[TorrentId]->SpeedRecorded > MaxsiTorrent_SpeedRememberFor ) { TorrentData[TorrentId]->SpeedRecorded = MaxsiTorrent_SpeedRememberFor; }
	}
	
	if ( TorrentData[TorrentId]->NextSpeedUpdate < Now )
	{
		ME_PostThreadMessage(WindowHWND,Torrent_MSG_SpeedUpdate,(LPARAM)this);

		TorrentData[TorrentId]->NextSpeedUpdate		=	Now + 100;
	}

	TorrentData[TorrentId]->ComThisSecond	+=	Compressed;
	TorrentData[TorrentId]->UncomThisSecond	+=	Uncompressed;

	return true;
}

__int64		ME_TorrentServer::GetCompressedSpeed(size_t TorrentId, size_t TimeAgo, size_t Smooth)
{
	if ( TorrentId == SIZE_MAX )
	{
		__int64		Sum		=	0;

		for ( size_t I = 0; I < nTorrents; I++ )
		{
			Sum		+=	GetCompressedSpeed(I,TimeAgo,Smooth);
		}

		return Sum;
	}

	if ( TimeAgo >= MaxsiTorrent_SpeedRememberFor ) { return ULLONG_MAX; }
	if ( TorrentId > nTorrents ) { return ULLONG_MAX; }	

	if ( Smooth == 0 ) { return TorrentData[TorrentId]->SpeedCompressed[TimeAgo]; }

	size_t	SmoothAvailable		=		(MaxsiTorrent_SpeedRememberFor-TimeAgo);

	__int64	Result	=	0;

	if ( SmoothAvailable > Smooth )			{ SmoothAvailable	=	Smooth; }
	if ( SmoothAvailable > TorrentData[TorrentId]->SpeedRecorded )	{ SmoothAvailable	=	TorrentData[TorrentId]->SpeedRecorded; }

	if ( SmoothAvailable == 0 ) { return 0; }
	
	for (size_t I = 0; I < SmoothAvailable; I++ )
	{
		Result	+=	TorrentData[TorrentId]->SpeedCompressed[TimeAgo+I];
	}

	return	Result/SmoothAvailable;
}


__int64		ME_TorrentServer::GetUncompressedSpeed(size_t TorrentId, size_t	TimeAgo, size_t Smooth)
{
	if ( TorrentId == SIZE_MAX )
	{
		__int64		Sum		=	0;

		for ( size_t I = 0; I < nTorrents; I++ )
		{
			Sum		+=	GetUncompressedSpeed(I,TimeAgo,Smooth);
		}

		return Sum;
	}

	if ( TimeAgo >= MaxsiTorrent_SpeedRememberFor ) { return ULLONG_MAX; }
	if ( TorrentId > nTorrents ) { return ULLONG_MAX; }	

	if ( Smooth == 0 ) { return TorrentData[TorrentId]->SpeedUncompressed[TimeAgo]; }

	size_t	SmoothAvailable		=		(MaxsiTorrent_SpeedRememberFor-TimeAgo);

	__int64	Result	=	0;

	if ( SmoothAvailable > Smooth )			{ SmoothAvailable	=	Smooth; }
	if ( SmoothAvailable > TorrentData[TorrentId]->SpeedRecorded )	{ SmoothAvailable	=	TorrentData[TorrentId]->SpeedRecorded; }

	if ( SmoothAvailable == 0 ) { return 0; }

	for (size_t I = 0; I < SmoothAvailable; I++ )
	{
		Result	+=	TorrentData[TorrentId]->SpeedUncompressed[TimeAgo+I];
	}

	return	Result/SmoothAvailable;
}

__int64		ME_TorrentServer::DataDownloaded(size_t TorrentId)
{
	if ( TorrentId == SIZE_MAX )
	{
		__int64		Sum		=	0;

		for ( size_t I = 0; I < nTorrents; I++ )
		{
			Sum		+=	DataDownloaded(I);
		}

		return Sum;
	}

	if ( TorrentId > nTorrents ) { return ULLONG_MAX; }	

	return TorrentData[TorrentId]->DataDownloaded;
}
__int64		ME_TorrentServer::DataTotal(size_t TorrentId)
{
	if ( TorrentId == SIZE_MAX )
	{
		__int64		Sum		=	0;

		for ( size_t I = 0; I < nTorrents; I++ )
		{
			Sum		+=	DataTotal(I);
		}

		return Sum;
	}

	if ( TorrentId > nTorrents ) { return ULLONG_MAX; }	

	return TorrentData[TorrentId]->DataTotal;
}

__int64		ME_TorrentServer::DataRequired(size_t TorrentId)
{
	if ( TorrentId == SIZE_MAX )
	{
		__int64		Sum		=	0;

		for ( size_t I = 0; I < nTorrents; I++ )
		{
			Sum		+=	DataRequired(I);
		}

		return Sum;
	}

	if ( TorrentId > nTorrents ) { return ULLONG_MAX; }	

	return TorrentData[TorrentId]->DataRequired;
}


bool	ME_TorrentServer::OnClientCreated	(ME_ServerClient* Client)
{
	ME_TorrentServerClient*		Peer		=	dynamic_cast<ME_TorrentServerClient*>(Client);
	if ( !Peer ) { return false; }

	// See if we can find an unconnected peer!

	size_t	TorrentId	=	SIZE_MAX;

	for (size_t I = 0; I < nTorrents; I++)
	{
		if ( PeersCreated < GetListSize(Peers) ) { TorrentId = I; break; }
	}

	if ( TorrentId == SIZE_MAX ) { return true; }

	char*	Instruction		=	(char*)GetListEntry(Peers,PeersCreated);
	int		Protocol		=	*(int*)Instruction;
			Instruction		+=	sizeof(int);

	Peer->SetProtocol(Protocol);
	Peer->Connect(Instruction);
	Peer->Init();
	
	PeersCreated++;

	return true;
}

bool	ME_TorrentServer::OnClientConnected	(SOCKET s, int Error)
{
	return	BaseClass::OnClientConnected(s,Error);
}

bool	ME_TorrentServer::OnClientDisconnected	(SOCKET s, int Error)
{
	return	BaseClass::OnClientDisconnected(s,Error);
}

bool	ME_TorrentServer::OnBufferExpandedFunc(ME_FileServer* Updated)
{
	for (size_t i = 0; i < MaxConnections; i++)
	{
		Clients[i]->Think();
	}
	return true;
}